#include <stdlib.h>
#include <string.h>

#include "qe_vars.h"
#include "qe_symbols.h"



struct q_symbolscopepool q_symbols = {NULL, NULL};

/* hash symbol name */
static int q_hashsymbol(const char* const symbol)
{
	int i, hash;
	for(i = 0, hash = 0; symbol[i]; ++i)
		hash += symbol[i];
	return hash % SYMBOLS_TABLE_SIZE;
}

/* check if symbol exists in choosen scope */
/* RETURN: symbol or NULL if no symbol found */
/* WARNING: no scope == NULL check */
static struct q_symbolcontainer* q_checkscope(const char* const symbol, struct q_symbolscope* scope)
{
	struct q_symbolcontainer* tmp = scope->table[q_hashsymbol(symbol)];
	while(tmp)
	{
		if(!strcmp(tmp->name, symbol))
			return tmp;
		tmp = tmp->next;
	}
	return NULL;
}

/* check if symbol exist in visible scope */
/* RETURN: symbol or NULL if no symbol found */
/* WARNING: no symbol == NULL check or q_symbols.global or q_symbols.current check */
static struct q_symbolcontainer* q_checkvisiblescope(const char* const symbol)
{
	struct q_symbolscope* lvl = q_symbols.current;
	while(lvl->type != SCOPE_GLOBAL)
	{
		struct q_symbolcontainer* tmp;
		if(tmp = q_checkscope(symbol, lvl))
			return tmp;
		lvl = lvl->prev;
	}
	/* now check globals */
	return q_checkscope(symbol, q_symbols.global);
}

/* initialize symbol scope pool */
/* RETURN: success - 0, failure - (-1) */
int q_initsymboltable()
{
	q_symbols.global = (struct q_symbolscope*)malloc(sizeof(struct q_symbolscope));
	if(!q_symbols.global)
		return -1;
	q_symbols.global->type = SCOPE_GLOBAL;
	memset(q_symbols.global->table, 0, sizeof(q_symbols.global->table));
	q_symbols.global->prev = NULL;
	q_symbols.current = q_symbols.global;

	/* add keywords here */


	return 0;
}



static int q_addkeyword(const char* const symbol)
{
	return 0;
}

/* TO DO: add functions to free particular symbol type */
static void q_freesymbolcontainer(struct q_symbolcontainer* symbol)
{
	if(!symbol)
		return;
	switch(symbol->type)
	{
		case SYMBOL_VARIABLE:
			q_freevariable(symbol->var);
			free(symbol->var);
			free(symbol);
			break;
		case SYMBOL_FUNCTION:
			free(symbol->func);
			free(symbol);
			break;
		case SYMBOL_STRUCTDEF:
			/* add handler here */
			/* free(symbol->structdef) */
			free(symbol);
			break;
		case SYMBOL_BUILDIN:
			free(symbol);
			break;
		default:
			/* fatal */
			break;
	}
}

/* frees scope */
static void q_freesymbolscope(struct q_symbolscope* scope)
{
	int i;
	for(i = 0; i < SYMBOLS_TABLE_SIZE; ++i)
	{
		struct q_symbolcontainer* tmp1 = scope->table[i];
		while(tmp1)
		{
			struct q_symbolcontainer* tmp2 = tmp1;
			tmp1 = tmp1->next;
			q_freesymbolcontainer(tmp2);
		}
	}
}

/* frees all symbols from all scopes */
void q_freesymbolscopepool()
{
	struct q_symbolscope* tmp1 = q_symbols.current;
	do
	{
		struct q_symbolscope* tmp2 = tmp1;
		tmp1 = tmp1->prev;
		q_freesymbolscope(tmp2);
	} while(tmp1);
	q_symbols.global = q_symbols.current = NULL;
}

/* add new scope and makes it current */
/* RETURN: 0 - ok, -1 - failure */
int q_addscopelevel(enum q_scopetype type)
{
	struct q_symbolscope* tmp;
	if(type == SCOPE_GLOBAL)
		return -1;
	if(!(tmp = (struct q_symbolscope*)malloc(sizeof(struct q_symbolscope))))
		return -1;
	tmp->type = type;
	memset(tmp->table, 0, sizeof(tmp->table));
	tmp->prev = q_symbols.current;
	q_symbols.current = tmp;
	return 0;
}

/* remove scope, makes higher lvl scope current */
/* RETURN: 0 - ok, -1 - failure */
int q_removescopelevel()
{
	struct q_symbolscope* tmp;
	if(q_symbols.current->type == SCOPE_GLOBAL)
		return -1;
	tmp = q_symbols.current;
	q_symbols.current = q_symbols.current->prev;
	q_freesymbolscope(tmp);
}

/* RETURN: ptr2variable or NULL if variable not found */
struct q_variable* q_getvariable(const char* const variable)
{
	struct q_symbolcontainer* tmp;
	if(tmp = q_checkvisiblescope(variable))
		if(tmp->type == SYMBOL_VARIABLE)
			return tmp->var;
	return NULL;
}
